package com.fanqiechaodan.springframework.context;

import com.fanqiechaodan.springframework.annotation.*;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.beans.Introspector;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @author fanqiechaodan
 * @Classname ApplicationContext
 * @Description
 * @Date 2023/2/21 20:26
 */
public class ApplicationContext {

    private Class configClass;

    /**
     * 储存BeanDefinition的Map,key:beanName;value:bean对应的信息
     */
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * 储存单例非懒加载的bean,key:beanName;value:bean实例
     */
    private Map<String, Object> singletonMap = new HashMap<>();


    /**
     * 文件分隔符
     */
    private static final String SEPARATOR = "/";

    public ApplicationContext(Class configClass) throws UnsupportedEncodingException {
        this.configClass = configClass;
        scan(configClass);
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
            if (BeanDefinition.SCOPE_SINGLETON.equals(beanDefinition.getScope()) && !beanDefinition.isLazy()) {
                // 先将单例非懒加载的bean创建储存起来
                Object bean = createBean(beanName, beanDefinition);
                singletonMap.put(beanName, bean);
            }
        });
    }


    /**
     * 返回一个bean实例
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) throws ClassNotFoundException {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (Objects.isNull(beanDefinition)) {
            throw new ClassNotFoundException();
        }
        if (BeanDefinition.SCOPE_SINGLETON.equals(beanDefinition.getScope())) {
            Object res = singletonMap.get(beanName);
            if (Objects.isNull(res)) {
                // map里面不存在bean实例,可能是懒加载第一次使用
                // 创建bean实例,并存到singletonMap
                res = createBean(beanName, beanDefinition);
                singletonMap.put(beanName, res);
            }
            return res;
        }
        // 多例bean每次都创建一个新的bean实例返回
        return createBean(beanName, beanDefinition);
    }

    /**
     * 扫描配置类
     *
     * @param configClass
     */
    private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value().replace(".", SEPARATOR);
            ClassLoader classLoader = this.getClass().getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());
            List<File> classFile = new ArrayList<>();
            addAllFile(file, classFile);
            for (File f : classFile) {
                String absolutePath = f.getAbsolutePath();
                absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                try {
                    Class<?> clazz = classLoader.loadClass(absolutePath.replace("\\", "."));
                    if (clazz.isAnnotationPresent(Component.class)) {
                        // 只扫描携带Component注解的类
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setType(clazz);
                        beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));
                        if (clazz.isAnnotationPresent(Scope.class)) {
                            beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                        } else {
                            // 没有使用Scope注解默认单例
                            beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
                        }
                        // 获取beanName
                        String beanName = clazz.getAnnotation(Component.class).value();
                        if (beanName.isEmpty()) {
                            // 注解中没有设置beanName时,将类名首字母小写作为beanName
                            beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建bean实例
     *
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();
        Object res = null;
        try {
            res = clazz.newInstance();
            for (Field filed : clazz.getDeclaredFields()) {
                // 依赖注入,这里做的比较简单,会有循环依赖问题
                if (filed.isAnnotationPresent(Autowried.class) && filed.getAnnotation(Autowried.class).required()) {
                    Object bean = getBean(filed.getName());
                    filed.setAccessible(Boolean.TRUE);
                    filed.set(res, bean);
                }
            }
            // 处理Aware回调
            if (res instanceof BeanNameAware) {

                ((BeanNameAware) res).setBeanName(beanName);
            }
            if (res instanceof ApplicationContextAware) {
                ((ApplicationContextAware) res).setApplicationContext(this);
            }
            // 处理事务
            if (clazz.isAnnotationPresent(Transactional.class)) {
                // cglib代理
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(clazz);
                Object target = res;
                enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
                    System.out.println("开启事务");
                    Object result = method.invoke(target, objects);
                    System.out.println("提交事务");
                    return result;
                });
                res = enhancer.create();
            }
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 将file文件夹下所有的文件都添加到classFile里面去
     *
     * @param file
     * @param classFile
     */
    private void addAllFile(File file, List<File> classFile) {
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                addAllFile(f, classFile);
            }
        } else {
            classFile.add(file);
        }
    }
}
